#ifdef WIN32
#include <Windows.h>
#endif

#include <iostream>

#include <SDL.h>
#include <SDL_mixer.h>

#include "glew/glew.h"

#include "Game.hpp"
#include "Audio.hpp"
#include "Color.hpp"

namespace fge
{

Game::Game()
	: screenSurface(NULL), screenWidth(800), screenHeight(600), isFullScreen(false),
		enableVerticalSync(true), audioFrequency(44100), audioChannels(2), audioChunkSize(1024),
		windowCaption("Fallen Game Engine"), exit(false)
{
	// Initialize variables only. No setup here!
	musicManager = NULL;

	for(int i=0; i<Key_Maximum; i++)
		lastKeyStates[i] = currentKeyStates[i] = false;
	for(int i=0; i<Mouse_Maximum; i++)
		lastMouseStates[i] = currentMouseStates[i] = false;
}

Game::~Game()
{
	delete musicManager;
}

void Game::Run()
{
	if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
	{
		// todo: error message
		return;
	}

	Initialize();

	if(Mix_OpenAudio(audioFrequency, AUDIO_S16SYS, audioChannels, audioChunkSize) != 0)
	{
		// todo: error message
		SDL_Quit();
		return;
	}

	if(Mix_AllocateChannels(32) != 32)
	{
		// todo: error message
		SDL_Quit();
		return;
	}

	SDL_WM_SetCaption(windowCaption.c_str(), "");

	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);

	if(!enableVerticalSync)
		SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 0);

	screenSurface = SDL_SetVideoMode(screenWidth, screenHeight, 32, SDL_OPENGL | (isFullScreen ? SDL_FULLSCREEN : 0));
	
	GLenum glewResult = glewInit();
	if(glewResult != GLEW_OK)
	{
		// Problem: glewInit failed, something is seriously wrong.
		std::cerr << "Error: " << glewGetErrorString(glewResult) << std::endl;
		return;
	}

	glClearColor(0, 0, 0, 1.0f);

	glViewport(0, 0, screenWidth, screenHeight);
	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	glOrtho(0, screenWidth, screenHeight, 0, 1, -1);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);
	glEnable(GL_TEXTURE_2D);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);

	musicManager = new MusicManager();

	LoadContent();

	uint32_t previousTicks = 0;

	while(!exit)
	{
		memcpy(lastMouseStates, currentMouseStates, Mouse_Maximum * sizeof(bool));
		memcpy(lastKeyStates, currentKeyStates, Key_Maximum * sizeof(bool));

		SDL_Event sdlEvent;
	    while (SDL_PollEvent(&sdlEvent))
		{
		    switch(sdlEvent.type)
			{
			case SDL_QUIT:
			    exit = true;
				break;
			case SDL_KEYDOWN:
				currentKeyStates[sdlEvent.key.keysym.sym] = true;
				break;
			case SDL_KEYUP:
				currentKeyStates[sdlEvent.key.keysym.sym] = false;
				break;
			case SDL_MOUSEBUTTONDOWN:
				currentMouseStates[sdlEvent.button.button] = true;
				break;
			case SDL_MOUSEBUTTONUP:
				currentMouseStates[sdlEvent.button.button] = false;
				break;
			case SDL_MOUSEMOTION:
				mouseX = sdlEvent.motion.x;
				mouseY = sdlEvent.motion.y;
				break;
			default:
			    break;
			}
		}

		uint32_t currentTicks = SDL_GetTicks();
		float deltaSeconds = 0;
		if(previousTicks != 0)
			deltaSeconds = (currentTicks - previousTicks) / 1000.0f;
		previousTicks = currentTicks;

		Update(deltaSeconds);

		glLoadIdentity();

		Draw();

		SDL_GL_SwapBuffers();
	}

	UnloadContent();

	Mix_CloseAudio();
	SDL_Quit();
}

void Game::ClearScreen(float r, float g, float b, float a)
{
	glClearColor(r, g, b, a);
	glClear(GL_COLOR_BUFFER_BIT);
}

void Game::ClearScreen(const Color &color)
{
	glClearColor(color.R(), color.G(), color.B(), color.A());
	glClear(GL_COLOR_BUFFER_BIT);
}

void Game::ShowCursor(bool value)
{
	SDL_ShowCursor(value ? SDL_ENABLE : SDL_DISABLE);
}

int Game::PlaySoundEffect(SoundEffect *soundEffect, bool loop)
{
	return Mix_PlayChannel(-1, soundEffect->GetSoundData(), loop ? -1 : 0);
}

void Game::PauseSoundEffect(int soundEffectHandle)
{
	Mix_Pause(soundEffectHandle);
}

void Game::FadeOutSoundEffect(int soundEffectHandle, float seconds)
{
	Mix_FadeOutChannel(soundEffectHandle, (int)(seconds * 1000));
}

void Game::StopSoundEffect(int soundEffectHandle)
{
	Mix_HaltChannel(soundEffectHandle);
}

}

